library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use WORK.constants.all;

entity CORE_LEFT is
	port (
		-- Systesm signals
		clk			: in std_logic;
		reset		: in std_logic;
		
		-- Input from coming from the processor
		data_in		:	in std_logic_vector(DATA_SIZE-1 downto 0);
		addr		:	in std_logic_vector(ADDR_SIZE-1 downto 0);
		rd_en		:	in std_logic;
		wr_en		:	in std_logic;
		
		-- Output signals going to the processor
		data_out	:	out std_logic_vector(DATA_SIZE-1 downto 0);
		req_irq		:	out std_logic;
		
		-- Output signals going to the monitoring chain : Core_Right
		shift		:	out std_logic;
		capture		:	out std_logic;
		update		:	out std_logic;
		gwen		:	out std_logic;
		gw_reset		:	out std_logic;
		hw_and_sw_reset : out std_logic;
		tdi			:	out std_logic;
		-- Input signal coming from the monitoring chain
		tdo			:	in std_logic
		);
end CORE_LEFT;

architecture RTL of CORE_LEFT is 

component ps_reg
	port (
		clk			: in std_logic;
		reset		: in std_logic;
		
		sel			: in std_logic;
		we			: in std_logic;
		
		parallel_in	: in std_logic_vector(DATA_SIZE-1 downto 0);
		serial_out	: out std_logic
	);
end component;

component REG32
	port (
		data_in		:	in std_logic_vector(DATA_SIZE-1 downto 0);
		data_out	:	out std_logic_vector(DATA_SIZE-1 downto 0);
		we			:	in std_logic;
		clk			:	in std_logic;
		reset		:	in std_logic
	);
end component;

component sp_reg
	port (
		clk			: in	std_logic;
		reset			: in	std_logic;
		we				: in	std_logic;
		serial_in		: in	std_logic;
		parallel_out	: out	std_logic_vector(DATA_SIZE-1 downto 0)
	);
end component;

component Res_Buffer
    Port ( clk : in  std_logic;
           reset : in  std_logic;
           data_in : in  std_logic_vector (DATA_SIZE-1 downto 0);
           store : in  std_logic;
           addr : in  std_logic_vector(ADDR_SIZE_RESBUFFER-1 downto 0);
           data_out : out  std_logic_vector (DATA_SIZE-1 downto 0));
end component;

component Counter_Nominal
	port(
		clk				: in std_logic;
		reset			: in std_logic;
		
		load			: in std_logic;
		seed			: in std_logic_vector(DATA_SIZE-1 downto 0);
		end_of_count	: out std_logic
	);
end component;

component COUNTER32
	port(
		clk				: in std_logic;
		reset			: in std_logic;
		
		load			: in std_logic;
		go_ok			: in std_logic;
		seed			: in std_logic_vector(DATA_SIZE-1 downto 0);
		mod32			: out std_logic;
		end_of_count	: out std_logic
	);
end component;

component STATE_MACHINE
	port (
		-- Systesm signals
		clk					: in std_logic;
		reset				: in std_logic;
		
		-- Input Signals
		launch_scan			:	in std_logic;
		scan_period_elapsed	:	in std_logic;
		end_gw_config		:	in std_logic;
		res_reg_buf_full	:	in std_logic;
		end_of_chain		:	in std_logic;
		irq_ack				:	in std_logic;
		
		-- Output signals
		shift				:	out std_logic;
		capture				:	out std_logic;
		update				:	out std_logic;
		gwen				:	out std_logic;
		resetn				:	out std_logic;
		req_irq				:	out std_logic;
		status				:	out std_logic_vector(STATUS_SIZE-1 downto 0)
	);
end component;

component Conf_Buffer
    Port ( clk : in  STD_LOGIC;
           reset : in  STD_LOGIC;
			  sel : in STD_LOGIC;
           addr_and_we : in  STD_LOGIC_VECTOR(ADDR_SIZE_CONFBUFFER-1 downto 0);
           load : in  STD_LOGIC;
           data_in : in  STD_LOGIC_VECTOR(DATA_SIZE-1 downto 0);
           data_out : out  STD_LOGIC_VECTOR(DATA_SIZE-1 downto 0));
end component;

	-- Write Enable Signals
	signal we_and_addr_cmd, we_and_addr_irq, we_and_addr_mon_len, we_and_addr_scan_period : std_logic;
	signal we_and_addr_config_buf : std_logic_vector(ADDR_SIZE_CONFBUFFER-1 downto 0);
	
	-- Read Enable Signals
	signal rd_and_addr_cmd, rd_and_addr_status, rd_and_addr_irq, rd_and_addr_mon_len, rd_and_addr_scan_period : std_logic;
	signal rd_and_addr_result_buffer : std_logic_vector(ADDR_SIZE_RESBUFFER-1 downto 0);
		
	-- fsm signals
	signal s_cmd, s_status, s_irq, s_irq_in, s_mon_len, s_scan_period : std_logic_vector(DATA_SIZE-1 downto 0);
	signal s_irq_we, s_req_irq : std_logic;
	
	signal fsm_status : std_logic_vector(DATA_SIZE-1 downto 0);
	signal status : std_logic_vector(STATUS_SIZE-1 downto 0);
	
	-- Monitoring length counter
	signal s_load_mon_len_counter : std_logic;
	signal s_mod32, s_end_of_count : std_logic;
	
	-- Configuration Counter
	signal s_config_seed : std_logic_vector(DATA_SIZE-1 downto 0);
	signal s_config_load : std_logic;
	
	signal s_config_end_of_count : std_logic;
	
	-- Elapsed Time Counter
	signal s_elpased_load : std_logic;
	signal s_elapsed_end_of_count : std_logic;
	
	-- command process signals
	signal s_cmd_reset, s_cmd_end_of_count, s_cmd_launch : std_logic := '0';
	
	-- Software Reset
	signal s_reset : std_logic;
	
	-- Shift Register Reset
	---- This signal is used the clean the content of the shift register between
	---- the monitoring cycle 
	signal shift_reg_reset : std_logic;
	
	-- Software stop
	signal s_end : std_logic;
	
	-- Configuration Parrallel Serial Register
	signal s_config_ps_we :std_logic;
	
	-- Configuration buffer signals
	signal s_config_buffer : std_logic_vector(DATA_SIZE-1 downto 0) := (others => '0');
	signal s_sel_config : std_logic;
	signal conf_sel: std_logic;
	signal s_config_advance :std_logic;

	-- Serial Parallel Register Signals
	signal s_result_sp_reg : std_logic_vector(DATA_SIZE-1 downto 0);
	signal result_sp_we : std_logic;
	
	-- Result Buffer Signals
	signal s_result_buffer	: std_logic_vector(DATA_SIZE-1 downto 0);
	signal s_result_we		: std_logic;
	
begin

	-- Write Enable and Register selection bit
	we_and_addr_cmd			<= wr_en and addr(CMD_ADDR);
	we_and_addr_irq			<= wr_en and addr(IRQ_ADDR);
	we_and_addr_mon_len		<= wr_en and addr(MON_LEN_ADDR);
	we_and_addr_scan_period	<= wr_en and addr(SCAN_PERIOD_ADDR);
	
	-- Read Enable and Register selection bit
	rd_and_addr_cmd			<= rd_en and addr(CMD_ADDR);
	rd_and_addr_status		<= rd_en and addr(STATUS_ADDR);
	rd_and_addr_irq			<= rd_en and addr(IRQ_ADDR);
	rd_and_addr_mon_len		<= rd_en and addr(MON_LEN_ADDR);
	rd_and_addr_scan_period	<= rd_en and addr(SCAN_PERIOD_ADDR);
	
	GEN_WE_AND_CONFBUFFER_ADDR :
	for J in 0 to ADDR_SIZE_CONFBUFFER-1 generate
		we_and_addr_config_buf(J)	<= wr_en and addr(CONFIG_BUFFER_BASE_ADDR+J); --* to be updated
	end generate;
	
	GEN_RD_AND_RESBUFFER_ADDR :
		for I in 0 to ADDR_SIZE_RESBUFFER-1 generate
			rd_and_addr_result_buffer(I) <= rd_en and addr(RESULT_BUFFER_BASE_ADDR+I);
		end generate;
	
	-- Command Signals
	s_reset         <= reset or (not s_cmd_reset);
	hw_and_sw_reset	<= reset or (not s_cmd_reset);
	
	s_end	<= s_cmd_end_of_count and s_end_of_count;

	---------------------
	-- Command Process --
	---------------------
	-- purpose : Convert the value int the command register into signals for the stat machine
	-- type    : Sequential
	P_COMMAND : process(s_cmd)
	begin
		case s_cmd is
			when CMD_START	=> 
				s_cmd_launch		<= '0';
				s_cmd_reset			<= '1';
				s_cmd_end_of_count	<= '1';
				
			when CMD_STOP	=>
				s_cmd_launch		<= '1';
				s_cmd_reset			<= '1';
				s_cmd_end_of_count	<= '0';
				
			when CMD_RESET	=>
				s_cmd_launch		<= '1';
				s_cmd_reset			<= '0';
				s_cmd_end_of_count	<= '1';
				
			when others =>
				s_cmd_launch		<= '1';
				s_cmd_reset			<= '1';
				s_cmd_end_of_count	<= '1';
		end case;
	end process;
	
	-------------------------------
	-- Monitoring length counter --
	-------------------------------
	-- purpose : Counts down to 0 using as initial value the content of the monitoring length regisdter. When reached the 0, some signals for the state machine are activated.
	-- type    : Structural
	monitoring_length_counter :
		counter32
		port map(
			clk				=> clk,
			reset			=> s_reset,
			seed			=> s_mon_len,
			go_ok			=> result_sp_we,
			load			=> s_load_mon_len_counter,
			mod32			=> s_mod32,
			end_of_count	=> s_end_of_count
		);
	
	---------------------------
	-- Configuration Counter --
	---------------------------
	-- purpose : Counts down to 0 using as initial value the number of gateway set during the design. When reached the 0, some signals for the state machine are activated.
	-- type    : Structural
	s_config_seed <= conv_std_logic_vector(N_GW, DATA_SIZE);
	Configuration_counter :
		Counter_Nominal
		port map(
			clk 			=> clk,
			reset 			=> s_reset,
			seed			=> s_config_seed,
			load			=> s_config_load,
			end_of_count	=> s_config_end_of_count
		);
	
	--------------------------
	-- Elapsed Time Counter --
	--------------------------
	-- purpose : Counts down to 0 using as initial value the content of scan period register. When reached the 0, some signals for the state machine are activated.
	-- type    : Structural
	Elapsed_Time_counter :
		Counter_Nominal
		port map(
			clk 			=> clk,
			reset 			=> s_reset,
			seed			=> s_scan_period,
			load			=> s_elpased_load,
			end_of_count 	=> s_elapsed_end_of_count
		);
	
	---------------------------
	-- Register's Definition --
	---------------------------
	-- purpose : Command register
	-- type    : Structural
	r_cmd :
		reg32
		port map (
			data_in		=> data_in,
			data_out	=> s_cmd,
			we			=> we_and_addr_cmd,
			clk			=> clk,
			reset		=> s_reset
		);
		
	-- purpose : Status register
	-- type    : Structural
	r_status :
		 reg32
		port map (
			data_in		=> fsm_status,
			data_out	=> s_status,
			we			=> '1',
			clk			=> clk,
			reset		=> s_reset
		);

	-- purpose : Select the input of irq register between the irq request coming from the state machine and the irq acknowledge coming from the processor.
	-- type    : Sequential
	req_irq <= s_req_irq;
	s_irq_in <= X"00000001" when s_req_irq = '1' else
					data_in when we_and_addr_irq = '1' else
					(others => '1');
	s_irq_we <= s_req_irq or we_and_addr_irq;

	-- purpose : Irq register
	-- type    : Structural
	r_irq :
		 reg32
		port map (
			data_in		=> s_irq_in,
			data_out	=> s_irq,
			we			=> s_irq_we,
			clk			=> clk,
			reset		=> s_reset
		);
	
	-- purpose : Monitoring length register
	-- type    : Structural
	r_mon_len :
		 reg32
		port map (
			data_in		=> data_in,
			data_out	=> s_mon_len,
			we			=> we_and_addr_mon_len,
			clk			=> clk,
			reset		=> s_reset
		);
	
	-- purpose : Scan period register
	-- type    : Structural
	r_scan_period :
		 reg32
		port map (
			data_in		=> data_in,
			data_out	=> s_scan_period,
			we			=> we_and_addr_scan_period,
			clk			=> clk,
			reset		=> s_reset
		);
		
	------------------------------
	----- Data Output Process ----
	------------------------------
	-- purpose : Output selection
	-- type    : Sequential
	data_out <=	s_cmd			when rd_and_addr_cmd			= '1' else
				s_status		when rd_and_addr_status			= '1' else
				s_irq			when rd_and_addr_irq			= '1' else
				s_mon_len		when rd_and_addr_mon_len		= '1' else
				s_scan_period	when rd_and_addr_scan_period	= '1' else
				s_result_buffer	when rd_and_addr_result_buffer(0)	= '1' else
				s_result_buffer	when rd_and_addr_result_buffer(1)	= '1' else
				s_result_buffer	when rd_and_addr_result_buffer(2)	= '1' else
				s_result_buffer	when rd_and_addr_result_buffer(3)	= '1' else
				(others => '0');
	
	-------------------
	-- State Machine --
	-------------------
	-- purpose : Generation of JTAG signal and status
	-- type    : Sequential
	fsm :
		 state_machine
		port map(
			clk => clk,
			reset => s_reset,
			
			-- Input Signals
			launch_scan			=> s_cmd_launch,
			scan_period_elapsed	=> s_elapsed_end_of_count,
			end_gw_config		=> s_config_end_of_count,
			res_reg_buf_full	=> s_mod32,
			end_of_chain		=> s_end,
			irq_ack				=> s_irq(0),
			
			-- Output signals
			shift				=> shift,
			capture				=> capture,
			update				=> update,
			gwen				=> gwen,
			resetn				=> gw_reset,
			req_irq				=> s_req_irq,
			status				=> status
		);
	
	-- generation of the status register value by sign extension
	-- this artefact is used to remove warning at the synthesis
	fsm_status <= status & status & status & status & status & status & status & status;
	
	----------------------------------------------------
	--- Configuration Parallel-Serial shift register ---
	----------------------------------------------------
	config_ps_reg :
		 ps_reg
		port map(
			clk		=> clk,
			reset	=> reset,
			sel		=> s_sel_config,
			we		=> s_config_ps_we,
			parallel_in	=> s_config_buffer,
			serial_out => tdi
		);
	
	----------------------------------------------------------
	--- Configuration Circular Buffer                      ---
	--- It has been implemented by a simple register       ---
	--- because we have just one address for configurarion ---
	----------------------------------------------------------
	configuration_buffer :
		Conf_Buffer
		 Port map ( 
			clk => clk,
			reset => reset,
			sel => conf_sel,
			addr_and_we	=>  we_and_addr_config_buf,
			load => s_config_advance,
			data_in => data_in,
			data_out => s_config_buffer
		);
	
	---------------------------------------------
	--- Result Serial Parallel Shift Register ---
	---------------------------------------------
	result_sp_reg :
		 sp_reg
		port map(
			clk				=> clk,
			reset			=> shift_reg_reset,
			we				=> result_sp_we,
			serial_in		=> tdo,
			parallel_out	=> s_result_sp_reg
		);
	
	------------------------------
	--- Result Circular Buffer ---
	------------------------------
	Result_Buffer :
		 Res_Buffer
		Port map ( 
			clk			=> clk,
			reset		=> reset,
			data_in		=> s_result_sp_reg,
			store		=> s_result_we,
			addr		=> rd_and_addr_result_buffer,
			data_out	=> s_result_buffer
		);
	
	------------------------------------------
	--- State Machine Status Decoder       ---
	--- It is used to generate activation  ---
	--- signals for the counters           ---
	--- Those signals go to '0' only for   ---
	--- stage in which the counter is used ---
	------------------------------------------
	P_STATE_DECODER :
		process( status, reset )
		begin
			case status is
				when STATUS_IDLE =>
					s_sel_config			<= '0';
					s_config_load			<= '0';
					s_config_ps_we			<= '0';
					conf_sel					<= '0';
					s_config_advance		<= '0';
					
					s_load_mon_len_counter	<= '0';
					s_elpased_load			<= '0';
					result_sp_we			<= '0';
					s_result_we				<= '0';
					shift_reg_reset			<= '1';
					
					
				when STATUS_GW_CONFIG =>
					s_sel_config			<= '0';
					s_config_load			<= '1';
					s_config_ps_we			<= '1';
					conf_sel          	<= '1';
					s_config_advance		<= '0';
					
					s_load_mon_len_counter	<= '0';
					s_elpased_load			<= '0';
					result_sp_we			<= '0';
					s_result_we				<= '0';
					shift_reg_reset			<= reset;
					
				when STATUS_GW_SHIFT =>
					s_sel_config			<= '1';
					s_config_load			<= '0';
					s_config_ps_we			<= '1';
					conf_sel          <= '0';
					s_config_advance		<= '0';
					
					s_load_mon_len_counter	<= '0';
					s_elpased_load			<= '0';
					result_sp_we			<= '0';
					s_result_we				<= '0';
					shift_reg_reset			<= reset;
					
				when STATUS_GW_UPDATE => 
					s_sel_config			<= '0';
					s_config_load			<= '0';
					s_config_ps_we			<= '1';
					conf_sel          <= '0';
					s_config_advance		<= '0';
					
					s_load_mon_len_counter	<= '0';
					s_elpased_load			<= '0';
					result_sp_we			<= '0';
					s_result_we				<= '0';
					shift_reg_reset			<= reset;

				when STATUS_MON_CAPTURE =>
					s_sel_config			<= '0';
					s_config_load			<= '0';
					s_config_ps_we			<= '0';
					conf_sel          <= '0';
					s_config_advance		<= '0';
					
					s_load_mon_len_counter	<= '1';
					s_elpased_load			<= '0';
					result_sp_we			<= '0';
					s_result_we				<= '0';
					shift_reg_reset			<= reset;
					s_config_advance		<= '0';
					
					
				when STATUS_MON_SHIFT =>
					s_sel_config			<= '1';
					s_config_load			<= '0';
					s_config_ps_we			<= '1';
					conf_sel          <= '0';
					s_config_advance		<= '0';
					
					s_load_mon_len_counter	<= '0';
					s_elpased_load			<= '0';
					result_sp_we			<= '1';
					s_result_we				<= '0';
					shift_reg_reset			<= reset;
			
				when STATUS_MON_UPDATE =>
					s_sel_config			<= '0';
					s_config_load			<= '0';
					s_config_ps_we			<= '0';
					conf_sel          <= '0';
					s_config_advance		<= '1';
					
					
					s_load_mon_len_counter	<= '0';
					s_elpased_load			<= '0';
					result_sp_we			<= '0';			
					s_result_we				<= '1';
					shift_reg_reset			<= reset;

					
				when STATUS_LOAD_CONFIG_STORE_RESULT =>
					s_sel_config			<= '0';
					s_config_load			<= '0';
					s_config_ps_we			<= '1';
					conf_sel          <= '0';
					s_config_advance		<= '1';
					
					s_load_mon_len_counter	<= '0';
					s_elpased_load			<= '0';
					result_sp_we			<= '0';					
					s_result_we				<= '1';
					shift_reg_reset			<= reset;

					
				when STATUS_IRQ =>
					s_sel_config			<= '0';
					s_config_load			<= '0';
					s_config_ps_we			<= '0';
					conf_sel          <= '0';
					s_config_advance		<= '0';
					

					s_load_mon_len_counter	<= '0';
					s_elpased_load			<= '1';
					result_sp_we			<= '0';
					s_result_we				<= '0';
					shift_reg_reset			<= reset;

					
				when others => 
					s_sel_config			<= '0';
					s_config_load			<= '0';
					s_config_ps_we			<= '0';
					conf_sel          <= '0';
					s_config_advance		<= '0';

					s_load_mon_len_counter	<= '0';
					s_elpased_load			<= '0';
					result_sp_we			<= '0';
					s_result_we				<= '0';
					shift_reg_reset			<= reset;
					
			end case;
		end process;
end;
